package errors

import (
	"fmt"
	"io"
	"strconv"
)

// [省略结构中的零值字段](https://github.com/xxjwxc/uber_go_guide_cn#%E7%9C%81%E7%95%A5%E7%BB%93%E6%9E%84%E4%B8%AD%E7%9A%84%E9%9B%B6%E5%80%BC%E5%AD%97%E6%AE%B5)

// New 返回一个基本错误，错误码默认为 0 （ 0 是内部保留的错误码），通过 WithCodeOpt
// 选项指定非 0 错误码。
func New(msg string, opts ...NewOption) error {
	// code 字段零值有效，所以省略。
	f := &fundamental{
		msg:   msg,
		stack: callers(),
	}

	for _, opt := range opts {
		opt.applyFundamental(f)
	}

	return f
}

// fundamental 定义了基本错误，由注解、错误码和调用栈组成。
type fundamental struct {
	*stack

	msg  string
	code int
}

// Error 方法设计：
// Error 返回错误的摘要信息， fundamental 是非包装错误，调用栈属于详细信息，因此
// 摘要由注解和错误码构成。对于包装错误，摘要信息中还应包含原始错误的摘要信息。

// Error 实现内建的 error 接口，参考 https://pkg.go.dev/builtin#error 。
func (f *fundamental) Error() string { return "msg:" + f.msg + ",code:" + strconv.Itoa(f.code) }

// Code 实现 codeWrapr 接口，返回错误码。
func (f *fundamental) Code() int { return f.code }

// Format 方法设计：
// Go fmt 包 API 设计借鉴了 C 语言，动词与标志具备一些约定，是实现 Formatter 接
// 口时必要的参考，对于错误打印来说，具备以下约定：
//
//	v%    摘要信息，等同于 %s ；
//	v+%   详细信息，包含摘要、错误码和每个栈帧的全部信息；
//	v-%   简化信息，包含摘要、错误码和每个栈帧的栈顶；
//	v#%   以 JSON 字符串输出简化信息；
//	s%    输出字符串表示，与 Error 方法的返回一致；
//	q%    输出双引号包裹的经过转义的字符串，外部安全。

// Format 实现 fmt.Formatter 接口，参考 https://pkg.go.dev/fmt#Formatter 。
func (f *fundamental) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		// 人类可读的调用栈输出实现委托 stack 。
		if s.Flag('+') || s.Flag('-') {
			io.WriteString(s, f.Error()+fmt.Sprintf(" [%s]", codeInfo(f.code)))
			f.stack.Format(s, verb)
			return
		}

		// 机器可读的 JSON 输出实现。
		if s.Flag('#') {
			return
		}
		// 执行下一个 case 。
		fallthrough
	case 's':
		// 输出字符串表示。
		io.WriteString(s, f.Error())
	case 'q':
		// 输出双引号包裹的转义后的字符串。
		fmt.Fprintf(s, "%q", f.Error())
	}
}

// withMessage 定义了包装注解的错误。
type withMessage struct {
	cause error
	msg   string
}

func (w *withMessage) Error() string { return "msg:" + f.msg + ",code:" + strconv.Itoa(f.code) }

// Cause 返回原始错误。
func (w *withMessage) Cause() error { return w.cause }

// Unwrap 兼容 go1.13 及以后的标准库实现，返回原始错误。
func (w *withMessage) Unwrap() error { return w.cause }

func (w *withMessage) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			fmt.Fprintf(s, "%+v\n", w.Cause())
			io.WriteString(s, w.Error())
			return
		}

		if s.Flag('-') {
			fmt.Fprintf(s, "%-v\n", w.Cause())
			io.WriteString(s, w.Error())
			return
		}

		if s.Flag('#') {
			return
		}

		fallthrough
	case 's':
		io.WriteString(s, w.Error())
	case 'q':
		fmt.Fprintf(s, "%q", w.Error())
	}
}

// withCode 定义了包装错误码的错误。
type withCode struct {
	cause error
	code  int
}

func (w *withCode) Error() string { return fmt.Sprintf("%v", w) }

func (w *withCode) Code() int { return w.code }

func (w *withCode) Cause() error { return w.cause }

// Unwrap 兼容 go1.13 及以后的标准库实现。
func (w *withCode) Unwrap() error { return w.cause }

// Format 实现了 fmt.Formatter 接口。
func (w *withCode) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			fmt.Fprintf(s, "%+v\n", w.Cause())
			io.WriteString(s, w.Error())
			io.WriteString(s, codeInfo(w.code))
			return
		}

		if s.Flag('-') {
			fmt.Fprintf(s, "%-v\n", w.Cause())
			io.WriteString(s, w.Error())
			io.WriteString(s, codeInfo(w.code))
			return
		}

		if s.Flag('#') {
			return
		}

		fallthrough
	case 's':
		io.WriteString(s, w.Error())
	case 'q':
		fmt.Fprintf(s, "%q", w.Error())
	}
}

// withStack 定义了包装调用栈的错误。
type withStack struct {
	error
	*stack
}

// Cause 返回包装前的错误。
func (w *withStack) Cause() error { return w.error }

// Unwrap 兼容 go1.13 及以后的标准库实现。
func (w *withStack) Unwrap() error { return w.error }

func (w *withStack) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			fmt.Fprintf(s, "%+v", w.Cause())
			w.stack.Format(s, verb)
			return
		}

		if s.Flag('-') {
			fmt.Fprintf(s, "%-v", w.Cause())
			w.stack.Format(s, verb)
			return
		}

		if s.Flag('#') {
			return
		}

		fallthrough
	case 's':
		io.WriteString(s, w.Error())
	case 'q':
		fmt.Fprintf(s, "%q", w.Error())
	}
}
